package com.ylr.aspect;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.annotation.Aspect;
import org.reactivestreams.Publisher;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.http.server.reactive.ServerHttpResponseDecorator;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.net.InetSocketAddress;
import java.net.URI;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.atomic.AtomicReference;

//
//Request Info [
//        ;header= [Host:"127.0.0.1",
//        Connection:"keep-alive",
//        Content-Length:"40",
//        sec-ch-ua:"" Not A;
//        Brand";v="99", "
//        Chromium";v="90", "Google Chrome";v="90"",
//        Accept:"application/json, text/plain, */*",
//        sec-ch-ua-mobile:"?0",
//        User-Agent:"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.85 Safari/537.36",
//        Content-Type:"application/json;charset=UTF-8",
//        Origin:"http://localhost:9527",
//        Sec-Fetch-Site:"cross-site", Sec-Fetch-Mode:"cors", Sec-Fetch-Dest:"empty", Referer:"http://localhost:9527/", Accept-Encoding:"gzip, deflate, br", Accept-Language:"zh-CN,zh;q=0.9"]
//        ;params=
//        ;address=127.0.0.153163
//        ;method=POST
//        ;url=/admin/login ]
//
//Request Info [
//        ;header= [User-Agent:"PostmanRuntime/7.26.8",
//        Accept:"*/*",
//        Postman-Token:"dddaff87-5d50-47cd-8c9e-fa4bfcf46fb4",
//        Host:"localhost:80", Accept-Encoding:"gzip, deflate, br",
//        Connection:"keep-alive",
//        Content-Type:"multipart/form-data; boundary=--------------------------531433487916487912666665",
//        Content-Length:"281"]
//        ;params=
//        ;address=0:0:0:0:0:0:0:153033
//        ;method=POST
//        ;url=/admin/login ]



@Slf4j
@Component
@Aspect
@Order(1)
public class AccessLogGlobalFilter implements GlobalFilter, Ordered {
 
    private static final String REQUEST_PREFIX = "Request Info [ ";
 
    private static final String REQUEST_TAIL = " ]";
 
    private static final String RESPONSE_PREFIX = "Response Info [ ";
 
    private static final String RESPONSE_TAIL = " ]";
 
    private StringBuilder normalMsg = new StringBuilder();
 
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        RecorderServerHttpRequestDecorator requestDecorator = new RecorderServerHttpRequestDecorator(request);
        InetSocketAddress address = requestDecorator.getRemoteAddress();
        HttpMethod method = requestDecorator.getMethod();
        URI url = requestDecorator.getURI();
        HttpHeaders headers = requestDecorator.getHeaders();
        Flux<DataBuffer> body = requestDecorator.getBody();
        //读取requestBody传参
        AtomicReference<String> requestBody = new AtomicReference<>("");
        body.subscribe(buffer -> {
            CharBuffer charBuffer = StandardCharsets.UTF_8.decode(buffer.asByteBuffer());
            requestBody.set(charBuffer.toString());
        });
        String requestParams = requestBody.get();
        normalMsg.append(REQUEST_PREFIX);
        normalMsg.append("\n;header= ").append(headers);
        normalMsg.append("\n;params=").append(requestParams);
        normalMsg.append("\n;address=").append(address.getHostName() + address.getPort());
        normalMsg.append("\n;method=").append(method.name());
        normalMsg.append("\n;url=").append(url.getPath());
        normalMsg.append(REQUEST_TAIL);
 
        ServerHttpResponse response = exchange.getResponse();
 
        DataBufferFactory bufferFactory = response.bufferFactory();
        normalMsg.append(RESPONSE_PREFIX);
        ServerHttpResponseDecorator decoratedResponse = new ServerHttpResponseDecorator(response) {
            @Override
            public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
                if (body instanceof Flux) {
                    Flux<? extends DataBuffer> fluxBody = (Flux<? extends DataBuffer>) body;
                    return super.writeWith(fluxBody.map(dataBuffer -> {
                        // probably should reuse buffers
                        byte[] content = new byte[dataBuffer.readableByteCount()];
                        dataBuffer.read(content);
                        String responseResult = new String(content, Charset.forName("UTF-8"));
                        normalMsg.append("\nstatus=").append(this.getStatusCode());
                        normalMsg.append("\n;header=").append(this.getHeaders());
//                        normalMsg.append(";responseResult=").append(responseResult);
                        normalMsg.append(RESPONSE_TAIL);
                        log.info(normalMsg.toString());
                        return bufferFactory.wrap(content);
                    }));
                }
                return super.writeWith(body); // if body is not a flux. never got there.
            }
        };
 
        return chain.filter(exchange.mutate().request(requestDecorator).response(decoratedResponse).build());
    }
 
    @Override
    public int getOrder() {
        return -2;
    }
 
}
